#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QPainter>
#include <QTimer>
#include <QKeyEvent>
#include <QPainterPath>
#include <QDebug>
#include <QMessageBox>
#include <cmath>
#include <ctime>
#include <thread>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    timer = nullptr;

    srand(time(nullptr));

    snakeBody.push_back({2, 5});
    snakeBody.push_back({2, 4});
    snakeBody.push_back({2, 3});
    snakeBody.push_back({2, 2});
    setFixedSize(440, 440);

    for(int i = 0; i < 20; ++i)
        for(int j = 0; j < 20; ++j)
            map[i][j] = TileType::EMPTY;

    map[2][2] = TileType::BODY;
    map[2][3] = TileType::BODY;
    map[2][4] = TileType::BODY;
    map[2][5] = TileType::BODY;
    direction = SnakeDirection::DOWN;
    generateFood();

    std::thread([this](){
        while(true){
            std::this_thread::sleep_for(std::chrono::milliseconds(500));
            update();
        }
    }).detach();
}

void MainWindow::paintEvent(QPaintEvent*)
{
    processSnake();
    paintSnake();
}

void MainWindow::keyPressEvent(QKeyEvent* event)
{
    if(event->key() == Qt::Key_W)
    {
        direction = SnakeDirection::UP;
    }
    else if(event->key() == Qt::Key_S)
    {
        direction = SnakeDirection::DOWN;
    }
    else if(event->key() == Qt::Key_A)
    {
        direction = SnakeDirection::LEFT;
    }
    else if(event->key() == Qt::Key_D)
    {
        direction = SnakeDirection::RIGHT;
    }
}

void MainWindow::paintSnake()
{
    QPainter painter(this);
    for(int i = 0; i < MAP_SIZE + 2; ++i)
    {
        for(int j = 0; j < MAP_SIZE + 2; ++j)
        {
            if(i == 0 || i == MAP_SIZE + 1 || j == 0 || j == MAP_SIZE + 1)
            {
                paintAt(i, j, Qt::black, painter);
            }
            else if(map[i - 1][j - 1] == TileType::BODY)
            {
                paintAt(i, j, Qt::blue, painter);
            }

        }
    }

    paintAt(foodPos.first + 1, foodPos.second + 1, Qt::red, painter);
}

void MainWindow::paintAt(int i, int j, Qt::GlobalColor color, QPainter& painter)
{
    QPainterPath path;
    QRect rect(i * 20, j * 20, 20, 20);
    path.addRect(rect);
    path.setFillRule(Qt::WindingFill);
    QPen pen;
    pen.setBrush(color);
    painter.setPen(pen);
    painter.drawPath(path);
    painter.fillRect(rect, color);
}

void MainWindow::processSnake()
{
    bool result;
    switch(direction)
    {
    case SnakeDirection::UP:
        result = moveSnakeTo(0, -1);
        break;
    case SnakeDirection::DOWN:
        result = moveSnakeTo(0, 1);
        break;
    case SnakeDirection::LEFT:
        result = moveSnakeTo(-1, 0);
        break;
    case SnakeDirection::RIGHT:
        result = moveSnakeTo(+1, 0);
        break;
    }
    if(!result)
    {
        QMessageBox msg(this);
        msg.setWindowTitle("啊哦...");
        msg.setText("蛇撞到障碍物了。");
        msg.exec();
        close();
    }
}

bool MainWindow::moveSnakeTo(int i, int j)
{
    i += snakeBody.front().first;
    j += snakeBody.front().second;
    if(i < 0 || i >= 20 || j < 0 || j >= 20 || map[i][j] == TileType::WALL)
    {
        return false;
    }

    else if(i == foodPos.first && j == foodPos.second)
    {
        //Eat food
        snakeBody.push_front({i, j});
        map[i][j] = TileType::BODY;
        generateFood();
    }

    else if(map[i][j] == TileType::WALL || map[i][j] == TileType::BODY)
    {
        return false;
    }

    else if(map[i][j] == TileType::EMPTY)
    {
        map[i][j] = TileType::BODY;
        snakeBody.push_front({i, j});
        map[snakeBody.back().first][snakeBody.back().second] = TileType::EMPTY;
        snakeBody.pop_back();
    }
    return true;
}

void MainWindow::generateFood()
{
    bool success = false;
    while(!success)
    {
        int x = rand() % 20;
        int y = rand() % 20;
        if(map[x][y] == TileType::EMPTY)
        {
            foodPos = {x, y};
            success = true;
        }
    }
}

MainWindow::~MainWindow()
{
    delete ui;
    delete timer;
}

